package com.leyou.order.service;

import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.threadlocals.UserHolder;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.entity.Order;
import com.leyou.order.entity.OrderDetail;
import com.leyou.order.entity.OrderLogistics;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.utils.PayHelper;
import com.leyou.order.vo.OrderDetailVO;
import com.leyou.order.vo.OrderLogisticsVO;
import com.leyou.order.vo.OrderVO;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @package com.leyou.order.service
 * @description:
 * @author: 许超越
 * @date 2019/7/12 11:59
 * @version: V1.0
 */
@Service
@Slf4j
public class OrderService {

    /**
     * redis中存放payUrl路径的key
     */
    private static final String KEY_PATTERN = "ly:wx:pay:";
    /**
     * 商城的描述信息
     */
    private static final String DESC = "乐优商城";
    /**
     * payUrl存放在Redis中的最大时长，单位小时
     */
    private static final long MAX_LIVE_TIME = 2;
    /**
     * 购物车付款订单移出发送消息的交换机名称
     */
    private static final String CART_EXCHANGE_NAME = "ly.cart.exchange";
    /**
     * 购物车付款订单移出购物车路由名称
     */
    private static final String CART_DOWN_KEY = "ly.cart.key";

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper detailMapper;

    @Autowired
    private OrderLogisticsMapper logisticsMapper;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private PayHelper payHelper;

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
     * 提交创建订单
     * @param orderDTO
     */
    @Transactional
    public Long createOrder(OrderDTO orderDTO) {
        //1.创建order
        Order order = new Order();

        //1.1生成订单编号
        long orderId = idWorker.nextId();
        order.setOrderId(orderId);

        //1.2获取当前登入用户的id
        Long userId = UserHolder.getUser().getId();
        order.setUserId(userId);

        //1.3设计相关金额
        List<CartDTO> carts = orderDTO.getCarts();
        //获取所有sku的id
        List<Long> skuIdList = carts.stream().map(CartDTO::getSkuId).collect(Collectors.toList());
        //获取一个map集合key是skuId，value是num
        Map<Long, Integer> map = carts.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
        //1.3.1查询所有sku
        List<SkuDTO> skuList = itemClient.querySkuByIds(skuIdList);
        //定义一个orderDetail的集合
        List<OrderDetail> details = new ArrayList<>();
        //1.3.2计算所有金额的和
        long total = 0;
        //遍历集合
        for (SkuDTO sku : skuList) {
            //获取当前遍历对象sku的num
            int num = map.get(sku.getId());
            //计算一种sku商品金额的总和
            total += sku.getPrice() * num;
            //封装orderDetail
            OrderDetail detail = new OrderDetail();
            detail.setTitle(sku.getTitle());
            detail.setSkuId(sku.getId());
            detail.setPrice(sku.getPrice());
            detail.setOwnSpec(sku.getOwnSpec());
            detail.setNum(num);
            detail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
            detail.setOrderId(orderId);
            details.add(detail);
        }
        //1.3.3设置金额相关数据
        //订单总金额
        order.setTotalFee(total);
        //邮费
        order.setPostFee(0L);
        //实际支付
        order.setActualFee(total + order.getPostFee());
        // //订单状态
        order.setPaymentType(orderDTO.getPaymentType());

        //1.4设置订单初始化状态
        order.setStatus(OrderStatusEnum.INIT.value());

        //1.5将order写入数据库
        int count = orderMapper.insertSelective(order);
        if (count != 1) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //2.将orderDetail数据写入数据库
        count = detailMapper.insertList(details);
        if (count != details.size()) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //3.写logistics
        //3.1查询地址
        AddressDTO address = userClient.queryAddressByUserIdAndId(orderDTO.getAddressId(), userId);
        //3.2填写物流信息
        OrderLogistics orderLogistics = BeanHelper.copyProperties(address, OrderLogistics.class);
        orderLogistics.setOrderId(orderId);
        //3.3写入数据库
        count = logisticsMapper.insertSelective(orderLogistics);
        if (count != 1) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //4.减少库存
        try {
            itemClient.minusStock(map);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH);
        }

        //5.发送mq消息
        Map<String, String> msg = new HashMap<>();
        msg.put("skuIdList", JsonUtils.toString(skuIdList));
        msg.put("userId", userId.toString());
        amqpTemplate.convertAndSend(CART_EXCHANGE_NAME, CART_DOWN_KEY, msg);

        //6.返回订单id
        return orderId;
    }


    /**
     * 通过订单id查询订单
     * @param orderId
     * @return
     */
    public OrderVO queryOrderById(Long orderId) {
        //1.查询订单
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order == null) {
            // 不存在
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //判断用户的id是否正确
        if (!UserHolder.getUser().getId().equals(order.getUserId())) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }

        //2.查询订单详情表
        OrderDetail detail = new OrderDetail();
        detail.setOrderId(orderId);
        List<OrderDetail> details = detailMapper.select(detail);
        if (CollectionUtils.isEmpty(details)) {
            throw new LyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);
        }

        //3.查询物流表
        OrderLogistics orderLogistics = logisticsMapper.selectByPrimaryKey(orderId);
        if (orderLogistics == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }

        // 4.封装数据
        OrderVO orderVO = BeanHelper.copyProperties(order, OrderVO.class);
        orderVO.setDetailList(BeanHelper.copyWithCollection(details, OrderDetailVO.class));
        orderVO.setLogistics(BeanHelper.copyProperties(orderLogistics, OrderLogisticsVO.class));
        return orderVO;
    }


    /**
     * 统一下单，获取支付二维码连接
     *
     * @param orderId 订单号
     * @return
     */
    public String createPayUrl(Long orderId) {
        //1.先从Redis中判断是否已经生成过支付路径
        String key = KEY_PATTERN + orderId;
        String cacheUrl = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNoneBlank(cacheUrl)) {
            return cacheUrl;
        }

        //2.查询订单
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order == null) {
            throw new LyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);
        }

        //3.判断订单的状态
        if (!order.getStatus().equals(OrderStatusEnum.INIT.value())) {
            //订单已经支付过
            throw new LyException(ExceptionEnum.INVALID_ORDER_STATUS);
        }

        //4.调用方法生成支付的二维码连接
        String payUrl = payHelper.getPayUrl(orderId, DESC, order.getActualFee());

        //5.将获取的二维码路径存入Redis中,设置时间2小时
        redisTemplate.opsForValue().set(key, payUrl, MAX_LIVE_TIME, TimeUnit.HOURS);

        //6.返回二维码支付连接路径
        return payUrl;
    }


    /**
     * 微信支付成功回调
     * @param data
     */
    @Transactional
    public void handleNotify(Map<String, String> data) {
        try {
            //1.业务校验
            payHelper.checkResultCode(data);

            //2.签名校验
            payHelper.checkSignature(data);

            //3.订单状态校验
            //商户订单号
            String outTradeNo = data.get("out_trade_no");
            //实际支付金额
            String totalFee = data.get("total_fee");
            //3.1校验订单号和实际支付金额是否为空
            if (StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalFee)) {
                //数据为空
                throw new RuntimeException("订单号或者实际支付金额为空！");
            }
            //3.2校验订单的状态
            Long orderId = Long.valueOf(outTradeNo);
            Order order = orderMapper.selectByPrimaryKey(orderId);
            if (!order.getStatus().equals(OrderStatusEnum.INIT.value())) {
                //订单已经支付过,重复通知
                return;
            }
            //3.3订单金额的校验
            if (!order.getActualFee().equals(Long.valueOf(totalFee))) {
                //订单金额错误
                throw new RuntimeException("订单金额有误！");
            }

            //4.修改订单状态
            Order record = new Order();
            record.setOrderId(orderId);
            record.setStatus(OrderStatusEnum.PAY_UP.value());
            record.setPayTime(new Date());
            int count = orderMapper.updateByPrimaryKeySelective(record);
            if (count != 1) {
                throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
            }
            log.info("处理微信支付通知成功！{}", data);

            // 5.删除Redis里面的值
            Boolean bool = redisTemplate.delete(KEY_PATTERN + orderId);
            if (bool == null || !bool) {
                throw new LyException(ExceptionEnum.DELETE_OPERATION_FAIL);
            }
        } catch (RuntimeException e) {
            // 出现异常，通知处理失败
            log.error("处理微信支付通知失败，原因：{}", e.getMessage());
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
    }


    /**
     * 获取订单支付状态
     *
     * @param orderId
     * @return
     */
    public Integer queryPayState(Long orderId) {
        //创建查询对应订单
        Order order = orderMapper.selectByPrimaryKey(orderId);
        //判断是否查询到
        if (order == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //返回订单状态
        return order.getStatus();
    }


    /**
     * 清理过时未付款订单
     * @param deadline
     */
    @Transactional
    public void closeOverdueOrders(Date deadline){
        //1.查询超时未支付的订单
        List<Long> orderIdList = orderMapper.queryOverdueUnpaidOrder(deadline);
        //1.1判断是否存在需要清理的订单
        if (CollectionUtils.isEmpty(orderIdList)) {
            log.info("【订单服务】没有需要清理的订单");
            return;
        }

        //2.关闭超时订单
        Order record = new Order();
        record.setCloseTime(new Date());
        record.setStatus(OrderStatusEnum.CLOSED.value());
        //2.1设置更新的条件
        Example example = new Example(Order.class);
        example.createCriteria().andIn("orderId", orderIdList);
        //2.2更新
        int count = orderMapper.updateByExampleSelective(record, example);
        log.info("【订单服务】关闭的订单数量：{}", count);
        if (count != orderIdList.size()) {
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }

        //3.恢复商品订单中的库存
        Example detailExample = new Example(OrderDetail.class);
        detailExample.createCriteria().andIn("orderId", orderIdList);
        //3.1查询商品的订单详情
        List<OrderDetail> details = detailMapper.selectByExample(detailExample);
        if (CollectionUtils.isEmpty(details)) {
            throw new LyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);
        }
        //3.2把订单商品的id作为key，数量作为value存进map集合中
        Map<Long, Integer> map = details.stream().collect(Collectors.toMap(OrderDetail::getSkuId, OrderDetail::getNum));
        //3.3调用商品微服务新增库存
        itemClient.plusStock(map);
    }
}
